package services

import (
	"fmt"
	"github.com/zw2582/ginlib"
	"go.uber.org/zap"
	"runtime/debug"
	"strings"
	"szxf/models"
	"szxf/vendors"
	"time"
)

type Bank4Service struct {
	LogReq *models.Bank4LogRequest
	logVendor []*models.Bank4LogVendor
	start time.Time
	account models.Account
}

var(
	VendorBank4Intans = map[int]vendors.VendorBank4 {
		1:vendors.VendorFqDefault,
		2:vendors.VendorNztDefault,
	}
)

//NewBank4Service 实例化一个实名认证服务
func NewBank4Service(accountId int, name, bankCardNo, idCardNo, phone, sign string) Bank4Service {
	return Bank4Service{
		LogReq:&models.Bank4LogRequest{
			AccountId: accountId,
			Uuid:      ginlib.OrderNo(""),
			Name:      name,
			BankCardNo:    bankCardNo,
			IdCardNo:idCardNo,
			Phone:phone,
			Sign:      sign,
			Code:      models.RC_UNKNOWN.Code(),
			Fake:      0,
			Error:     "",
			FromCache: 1,
			CreatedAt: time.Now(),
		},
		start:time.Now(),
		logVendor:make([]*models.Bank4LogVendor, 0),
		account:models.AccountGet(accountId),
	}
}

func (this *Bank4Service) Check() (rc models.ResultCode) {
	//收尾清理结果
	defer this.CheckEnd()

	rc = this.CheckDo()
	if rc == nil || rc.Code() == 0 {
		rc = models.RC_UNKNOWN
		this.LogReq.Fake = 1
	}
	this.LogReq.Code = rc.Code()

	return rc
}

//执行请求
func (this *Bank4Service) CheckDo() (rc models.ResultCode) {
	//校验参数,有代号返回说明有错
	if rc := this.Valid(); rc != nil {
		return rc
	}

	//查看缓存数据
	requestCacheKey := fmt.Sprintf("Bank4_request_cache:name:%s:bankCardNo:%s:idCardNo:%s:phone:%s", this.LogReq.Name,this.LogReq.BankCardNo, this.LogReq.IdCardNo, this.LogReq.Phone)
	var rcCache models.RcBank
	ginlib.RedisCache(requestCacheKey, &rcCache, time.Hour, func() (i interface{}) {
		this.LogReq.FromCache = 0
		rc, err := this.VendorCheck()
		if err != nil {
			this.LogReq.Fake = 1
			this.LogReq.Error = err.Error()
			ginlib.Logger.Error(err.Error())
		}
		return rc
	})
	rc = rcCache

	//合法返回值则扣除余额
	if ginlib.IndexOf([]models.ResultCode{models.RC_BANK_YIZHI, models.RC_BANK_NOT_YIZHI}, rc) != -1 {
		models.AccountDecr(this.account.Id, this.account.PriceBank4)
	}

	//返回校验结果
	return rc
}

//Valid 校验参数
func (this *Bank4Service) Valid() models.ResultCode {
	//检验必选参数
	if this.LogReq.Name == "" {
		return models.RC_BANK_ELLEGAL_NAME
	}
	if this.LogReq.BankCardNo == "" {
		return models.RC_BANK_ELLEGAL_CARD_BANK
	}
	if this.LogReq.IdCardNo == "" {
		return models.RC_BANK_ELLEGAL_CARD_ID
	}
	if this.LogReq.Phone == "" {
		return models.RC_BANK_ELLEGAL_PHONE
	}
	//校验商家是否存在
	account := this.account
	if account.Id == 0 {
		return models.RC_NO_ACCOUNT
	}
	//校验余额是否充足
	if account.MonthCheck == 0 && account.Balance < account.PriceBank4 {
		return models.RC_NO_MONEY
	}
	this.LogReq.Price = account.PriceBank4

	//校验sign是否正确
	signed := false
	if strings.ToLower(this.LogReq.Sign) == strings.ToLower(account.AppKey) {
		signed = true
	}
	if account.Id == 100001 && strings.ToLower(this.LogReq.Sign) == "xs323kjsiu9sdgnlfgjhjlnlfskdjfsd" {
		signed = true
	}
	if !signed {
		return models.RC_SIGN_FAIL
	}

	//校验身份证号码是否正确
	idcard := []byte(this.LogReq.IdCardNo)
	if !ginlib.IsValidCitizenNo(&idcard) {
		return models.RC_BANK_ELLEGAL_CARD_ID
	}
	return nil
}

//VendorCheck 通道请求
func (this *Bank4Service) VendorCheck() (rc models.ResultCode, err error){
	//本地校验
	if tmpRc := models.BankDataCheck(this.LogReq.Name, this.LogReq.BankCardNo, this.LogReq.IdCardNo, this.LogReq.Phone); tmpRc != nil {
		this.LogReq.FromCache = 2
		return tmpRc, err
	}

	//获取有用通道列表
	this.LogReq.FromCache = 0
	validVendorIds := this.GetValidVendorIds()

	for _, vendorId := range validVendorIds {
		//若有错误或库无，则循环使用通道测试
		rc, err = this.VendorCheckDo(vendorId)
		if err != nil {
			continue
		}
		break
	}

	return
}

//VendorCheckDo 通道详细请求
func (this *Bank4Service) VendorCheckDo(vendorId int) (rc models.ResultCode, err error) {
	paramRaw := []byte{}
	resRaw := []byte{}
	start := time.Now()
	end := start

	defer func() {
		if e := recover(); e != nil {
			switch val := e.(type) {
			case error:
				err = val
			default:
				err = fmt.Errorf("%+v", err)
			}
			ginlib.Logger.Error(err.Error(), zap.String("Stack", string(debug.Stack())))
		}
		//记录通道结果
		vendorLog := &models.Bank4LogVendor{
			Uuid:       this.LogReq.Uuid,
			VendorId:vendorId,
			VendorReq:  string(paramRaw),
			VendorResp: string(resRaw),
			Duration:   end.Sub(start).Milliseconds(),
			Status:     1,
		}
		if rc != nil {
			vendorLog.Code = rc.Code()
		}
		if err != nil {
			vendorLog.Error = err.Error()
			vendorLog.Status = 2
			//下线通道
			this.OffVendor(vendorId)
		}
		this.logVendor = append(this.logVendor, vendorLog)
	}()
	//请求通道数据
	vendor := VendorBank4Intans[vendorId]

	start = time.Now()
	paramRaw, resRaw, rc, err = vendor.Bank4Check(this.LogReq.Name, this.LogReq.BankCardNo, this.LogReq.IdCardNo, this.LogReq.Phone)
	end = time.Now()

	return
}

func (this *Bank4Service) OffVendor(vendorId int) {
	cacheKey := fmt.Sprintf("bank4:appname:%s:off_vendor_id:%d", ginlib.APP_NAME, vendorId)
	//下线5分钟
	ginlib.RedisCli.SetNX(cacheKey, "1", time.Minute*5)
}

//判断通道是否下线
func (this *Bank4Service) VendorIsOffed(vendorId int) bool {
	cacheKey := fmt.Sprintf("bank4:appname:%s:off_vendor_id:%d", ginlib.APP_NAME, vendorId)
	return ginlib.RedisCli.Get(cacheKey).Val() == "1"
}

//GetVendor 排除下架通道，获取在线通道
func (this *Bank4Service) GetValidVendorIds() []int {
	//排除下架通道，获取在线通道
	validVendorIds := make([]int, 0)
	for vendorId,_ := range VendorBank4Intans {
		if !this.VendorIsOffed(vendorId) {
			validVendorIds = append(validVendorIds, vendorId)
		}
	}
	//如果全部没有则兜底
	if len(validVendorIds) == 0 {
		validVendorIds = append(validVendorIds, 1)
	}
	ginlib.Logger.Info("在线通道：", zap.Ints("在线通道id", validVendorIds))
	return validVendorIds
}

//CheckEnd 检查结束
func (this *Bank4Service) CheckEnd() {
	if e := recover(); e != nil {
		var err error
		switch val := e.(type) {
		case error:
			err = val
		default:
			err = fmt.Errorf("%+v", err)
		}
		this.LogReq.Fake = 1
		this.LogReq.Error = err.Error()
		ginlib.Logger.Error(err.Error(), zap.String("Stack", string(debug.Stack())))
	}
	//执行时间
	this.LogReq.Duration = time.Now().Sub(this.start).Milliseconds()

	//保存logRequest
	ginlib.Logger.Debug("CheckEnd 保存logRequest", zap.Any("logRequest", this.LogReq))
	dbQueueSer := NewDbQueueService()
	dbQueueSer.Push(this.LogReq)

	//保存logVendor
	for _,vendorLog := range this.logVendor {
		dbQueueSer.Push(vendorLog)
	}
}

